﻿using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using ServiceKit.Audit;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace ServiceKit.WebApi
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
    public class ActionAuditFilterAttribute : ActionFilterAttribute
    {
        public static List<Type> IgnoredTypesForSerializationOnAuditLogging { get; }

        public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
        {
            var auditData = AuditFilterData.GetOrNull(actionExecutedContext);
            if (auditData == null)
            {
                return;
            }
            auditData.Stopwatch.Stop();
            auditData.AuditInfo.EndTime = DateTime.Now;
            auditData.AuditInfo.ExecutionDuration = Convert.ToInt32(auditData.Stopwatch.Elapsed.TotalMilliseconds);
            if (actionExecutedContext != null && actionExecutedContext.Exception != null)
            {
                auditData.AuditInfo.HasException = true;
                auditData.AuditInfo.Exception = actionExecutedContext.Exception.ToString();
            }
            //Log.Save(auditData.AuditInfo);
            base.OnActionExecuted(actionExecutedContext);
        }

        public override  void OnActionExecuting(ActionExecutingContext actionContext)
        {
            var auditInfo = CreateAuditInfo(actionContext);
            var actionStopwatch = Stopwatch.StartNew();
            var stopwatch = Stopwatch.StartNew();
            AuditFilterData.Set(
                actionContext,
                new AuditFilterData(
                    actionStopwatch,
                    auditInfo
                )
            );
            base.OnActionExecuting(actionContext);
        }

        private AuditInfo CreateAuditInfo(ActionExecutingContext context)
        {
            var auditInfo = new AuditInfo
            {
                BeginTime = DateTime.Now,
                ActionName = ((Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)context.ActionDescriptor).ActionName
            };
            if (context.HttpContext.Request.Path.Value.StartsWith("/api/"))
            {
                string[] routes = context.HttpContext.Request.Path.Value.Split('/');
                if (routes.Length > 3)
                {
                    auditInfo.ModuleName = routes[2];
                }
            }
            auditInfo.ActionUrl = context.HttpContext.Request.GetDisplayUrl();

            //auditInfo.ActionUrl = context.RouteData.Route.RouteTemplate.Replace("{controller}", auditInfo.ServiceName);
            return auditInfo;
        }

        private string ConvertArgumentsToJson(IDictionary<string, object> arguments)
        {
            try
            {
                if (arguments == null || arguments.Count == 0)
                {
                    return "{}";
                }

                var dictionary = new Dictionary<string, object>();

                foreach (var argument in arguments)
                {
                    if (argument.Value != null && IgnoredTypesForSerializationOnAuditLogging.Any(t => t.IsInstanceOfType(argument.Value)))
                    {
                        dictionary[argument.Key] = null;
                    }
                    else
                    {
                        dictionary[argument.Key] = argument.Value;
                    }
                }

                return Serialize(dictionary);
            }
            catch (Exception ex)
            {
                return "{}";
            }
        }

        private string Serialize(object obj)
        {
            return JsonConvert.SerializeObject(obj);
        }

        private string GetServiceName(Type type)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(DynamicApiController<>))
            {
                var interfaceType = type.GetGenericArguments()[0];
                return interfaceType.GetConventionalServiceName();
            }
            else
            {
                return type.Name.ToString();
            }
        }
    }
}
